#include <stdio.h>
#include <glib.h>
#include <kewinternal.h>

#define source_file_name "Integer"

#define value(OBJECT) ((int)((OBJECT)->context[0]))
#define check_int(VAL) KewCheckType((VAL), &KewIntType, "integer")

#define DEFINE_INT_OP(OPNAME,OP) \
METHOD(OPNAME) \
{ \
  check_int(args[0]); \
  results[0] = KewNewInt(value(receiver) OP value(args[0]));\
}

#define DEFINE_BOOL_OP(OPNAME,OP) \
METHOD(OPNAME) \
{ \
  check_int(args[0]); \
  results[0] = KewNewBool(value(receiver) OP value(args[0])); \
}


DEFINE_INT_OP(Plus,+)
DEFINE_INT_OP(Minus,-)
DEFINE_INT_OP(Multiply,*)
DEFINE_INT_OP(Divide,/)
DEFINE_INT_OP(Modulo,%)

DEFINE_BOOL_OP(Less,<)
DEFINE_BOOL_OP(LessEqual,<=)
DEFINE_BOOL_OP(Greater,>)
DEFINE_BOOL_OP(GreaterEqual,>)
DEFINE_BOOL_OP(Equal,==)
DEFINE_BOOL_OP(NotEqual,!=)

METHOD(Max)
{
  check_int(args[0]);
  results[0] = KewNewInt(MAX(value(receiver), value(args[0])));
}

METHOD(Min)
{
  check_int(args[0]);
  results[0] = KewNewInt(MIN(value(receiver), value(args[0])));
}

METHOD(Abs)
{
  results[0] = KewNewInt(ABS(value(receiver)));
}

METHOD(Clamp)
{
  check_int(args[0]);
  check_int(args[1]);
  results[0] = KewNewInt(CLAMP(value(receiver), value(args[0]), value(args[1])));
}

METHOD(Hash)
{
  results[0] = receiver;
}


METHOD(String)
{
  results[0] = KewWrapString(g_strdup_printf("%u", value(receiver)));
}


METHOD(To)
{
  check_int(args[0]);
  results[0] = KewNewRange(receiver, args[0]);
}

METHOD(Integer)
{
  results[0] = receiver;
}

METHOD(Real)
{
  results[0] = KewNewReal((double) value(receiver));
}

static KewObjectType KewIntType = {
  source_file_name, __LINE__,
  1,
  {
    {"hash", Hash, 0, 1},
    {"+", Plus, 1, 1},
    {"-", Minus, 1, 1},
    {"*", Multiply, 1, 1},
    {"/", Divide, 1, 1},
    {"%", Modulo, 1, 1},
    {"<", Less, 1, 1},
    {"<=", LessEqual, 1, 1},
    {">", Greater, 1, 1},
    {">=", GreaterEqual, 1, 1},
    {"=", Equal, 1, 1},
    {"!=", NotEqual, 1, 1},
    {"max", Max, 1, 1},
    {"min", Min, 1, 1},
    {"abs", Abs, 0, 1},
    {"clamp", Clamp, 2, 1},
    {"string", String, 0, 1},
    {"to", To, 1, 1},
    {"integer", Integer, 0, 1},
    {"real", Real, 0, 1},
    {NULL, NULL, 0, 0}
  }
};


KewObject *
KewNewInt(int i)
{
  KewObject *result;

  result = KewNewObject(&KewIntType);
  value(result) = i;
  return result;
}

int
KewIntVal(KewObject *object)
{
  KewCheckType(object, &KewIntType, "integer");
  return value(object);
}
